package com.sunwayworld.basemodule.samplemanage.location.service.impl;

import com.sunwayworld.basemodule.common.constant.LimsConstant;
import com.sunwayworld.basemodule.samplemanage.change.bean.LimsLocationChangeBean;
import com.sunwayworld.basemodule.samplemanage.change.service.LimsLocationChangeService;
import com.sunwayworld.basemodule.samplemanage.location.bean.LimsLocationBoardHoleBean;
import com.sunwayworld.basemodule.samplemanage.location.service.LimsLocationBoardHoleService;
import com.sunwayworld.basemodule.samplemanage.location.service.LimsLocationService;
import com.sunwayworld.basemodule.samplemanage.sample.bean.LimsSampleBean;
import com.sunwayworld.basemodule.samplemanage.sample.service.LimsSampleService;
import com.sunwayworld.framework.database.sql.Order;
import com.sunwayworld.framework.mybatis.mapper.MapperParameter;
import com.sunwayworld.framework.mybatis.mapper.MatchPattern;
import com.sunwayworld.framework.mybatis.mapper.SearchFilter;
import com.sunwayworld.framework.support.PersistableHelper;
import com.sunwayworld.framework.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.sunwayworld.framework.at.annotation.AuditTrailType;
import com.sunwayworld.framework.at.annotation.AuditTrailEntry;
import com.sunwayworld.framework.spring.annotation.GikamBean;

import com.sunwayworld.basemodule.samplemanage.location.dao.LimsLocationBoardDao;
import com.sunwayworld.basemodule.samplemanage.location.bean.LimsLocationBoardBean;
import com.sunwayworld.basemodule.samplemanage.location.service.LimsLocationBoardService;
import com.sunwayworld.framework.context.ApplicationContextHelper;
import com.sunwayworld.framework.restful.data.RestJsonWrapperBean;

import java.util.*;
import java.util.stream.Collectors;

@Repository
@GikamBean
public class LimsLocationBoardServiceImpl implements LimsLocationBoardService {

    @Autowired
    private LimsLocationBoardDao limsLocationBoardDao;

    @Autowired
    @Lazy
    private LimsLocationBoardHoleService holeService;

    @Autowired
    @Lazy
    private LimsLocationChangeService locationChangeService;

    @Autowired
    @Lazy
    private LimsSampleService sampleService;

    @Autowired
    @Lazy
    private LimsLocationService limsLocationService;

    @Override
    @SuppressWarnings("unchecked")
    public LimsLocationBoardDao getDao() {
        return limsLocationBoardDao;
    }

    @Override
    @Transactional
    @AuditTrailEntry(AuditTrailType.INSERT)
    public Long insert(RestJsonWrapperBean jsonWrapper) {
        LimsLocationBoardBean boardBean = jsonWrapper.parseUnique(LimsLocationBoardBean.class);
        boardBean.setId(ApplicationContextHelper.getNextIdentity());
        boardBean.setTotalHoleCount(boardBean.getColumnNum() * boardBean.getRowNum());
        getDao().insert(boardBean);
        holeService.insertHole(boardBean); // 关联插入孔位
        return boardBean.getId();
    }

    @Transactional
    @Override
    public void delete(RestJsonWrapperBean wrapper) {
        List<LimsLocationBoardBean> deletes = wrapper.parse(LimsLocationBoardBean.class);
        List<Long> ids = deletes.stream().map(LimsLocationBoardBean::getId).collect(Collectors.toList());
        holeService.batchDeleteByBoardId(ids); // 删除孔位
        getDao().deleteByIdList(ids);
    }

    @Transactional
    @Override
    public String updateSampleLocations(RestJsonWrapperBean wrapper) {
        List<LimsLocationBoardBean> boardBeans = wrapper.parse(LimsLocationBoardBean.class, 0);// 板
        List<LimsSampleBean> parses = wrapper.parse(LimsSampleBean.class, 1);// 样本
        List<Long> sampleIds = parses.stream().filter(s -> ObjectUtils.isEmpty(s.getBoardId())).map(LimsSampleBean::getId).distinct()
                .collect(Collectors.toList());  // 跳过已经分配的
        // 单表查询
        List<LimsSampleBean> sampleBeans = sampleService.getDao().selectListByIds(sampleIds, Order.desc("id"));

        // 通过选中的位置，找到位置下的孔，顺序分配孔位
        List<Long> ids = boardBeans.stream().map(LimsLocationBoardBean::getId).collect(Collectors.toList());
        String assignType = wrapper.getParamValue("assignType");
        // 获取起始孔位
        int row = NumberUtils.parseInt(wrapper.getParamValue("row"));// 行表示y轴
        int col = NumberUtils.parseInt(wrapper.getParamValue("col"));// 列表示x轴
//        String start
        List<LimsLocationBoardHoleBean> holeBeans;
        if("y".equalsIgnoreCase(assignType)){ // 竖着排
            holeBeans = holeService.selectListByFilter(SearchFilter.instance()
                            .match("boardId", ids).filter(MatchPattern.OR)
                            .match("stockType", "free").filter(MatchPattern.EQ)
                    , Order.asc("x"), Order.asc("y"));
            // 判断可分配的孔位是否足够
            if (holeBeans.size() < sampleBeans.size()) {
                return "库位不足";
            }
            // 纵向排列时，排除孔位（x坐标小于起始x坐标、x坐标相等y坐标小于起始y坐标）
            holeBeans.removeIf(e -> (e.getX() < col) || (e.getX() == col && e.getY() < row));
        } else { // 横着排
            holeBeans = holeService.selectListByFilter(SearchFilter.instance()
                            .match("boardId", ids).filter(MatchPattern.OR)
                            .match("stockType", "free").filter(MatchPattern.EQ)
                    , Order.asc("y"), Order.asc("x"));
            // 判断可分配的孔位是否足够
            if (holeBeans.size() < sampleBeans.size()) {
                return "库位不足";
            }
            // 横向排列时，排除孔位（y坐标小于起始y坐标、y坐标相等x坐标小于起始x坐标）
            holeBeans.removeIf(e -> (e.getY() < row) || (e.getY() == row && e.getX() < col));
        }
        // 判断可分配的孔位是否足够
        if (holeBeans.size() < sampleBeans.size()) {
            return "库位不足";
        }

        List<LimsSampleBean> updateSampleBeans = new ArrayList<>();
        List<LimsLocationBoardHoleBean> updateLocationHoleBeans = new ArrayList<>();
        List<Long> changeIds = ApplicationContextHelper.getNextIdentityList(sampleBeans.size());
        List<LimsLocationChangeBean> locationChangeBeans = new ArrayList<>();
        Map<String, List<LimsLocationChangeBean>> oldLocationChangeBeans = locationChangeService.selectListByFilter(SearchFilter.instance()
                .match("businessId", sampleIds).filter(MatchPattern.OR))
                .stream().collect(Collectors.groupingBy(LimsLocationChangeBean :: getBusinessId));
        // 是否修改样本实验编码，参数change=0，不修改
        String change = wrapper.getParamValue("change");
        // 给每个样本 分配孔位
        for (int i = 0; i < sampleBeans.size(); i++) {
            LimsSampleBean sampleBean = sampleBeans.get(i);

            LimsLocationBoardHoleBean holeBean = holeBeans.get(i);
            holeBean.setSampleId(sampleBean.getId());
            holeBean.setStockType("in");
            updateLocationHoleBeans.add(holeBean);

            sampleBean.setBoardHoleId(holeBean.getId());
            sampleBean.setBoardId(holeBean.getBoardId());
            sampleBean.setLocations(holeBean.getLocations());
            // 不为0则修改
            if (!"0".equals(change)) {
                // 生成实验编号：T-板编号-位置号（先横后竖，例如：T-01-01B），根据locations：板编号(竖横)，例如板01(A01)
                if (!StringUtils.isEmpty(holeBean.getLocations())) {
                    String locations = holeBean.getLocations();
                    String board = locations.split("\\(")[0];
                    // 位置号
                    String hole = locations.split("\\(")[1];
                    sampleBean.setExpNumber("T-" + board + "-" + hole.substring(1,3) + hole.substring(0,1));
                }
            }
            updateSampleBeans.add(sampleBean);

            LimsLocationChangeBean locationChangeBean = new LimsLocationChangeBean();
            locationChangeBean.setId(changeIds.remove(0));
            LimsLocationBoardBean boardBean = boardBeans.stream().filter(bb -> bb.getId().equals(holeBean.getBoardId())).findFirst().get();
            locationChangeBean.setNewValue(boardBean.getExt$Item("locationdesc") + "-" + sampleBean.getLocations());
            List<LimsLocationChangeBean> oldBeans = oldLocationChangeBeans.get(sampleBean.getId().toString());
            if (oldBeans != null && oldBeans.size() > 0){
                String oldValue = oldBeans.stream().sorted(Comparator.comparing(LimsLocationChangeBean::getCreatedTime).reversed()).collect(Collectors.toList()).get(0).getNewValue();
                locationChangeBean.setOldValue(oldValue);
            }
            locationChangeBean.setBusinessId(sampleBean.getId().toString());
            locationChangeBean.setBusinessCode("T_LIMS_SAMPLE");
            locationChangeBeans.add(locationChangeBean);
        }
        if (!"0".equals(change)) {
            sampleService.getDao().update(updateSampleBeans, ArrayUtils.asList("boardId", "boardHoleId", "locations", "expNumber"));
        } else {
            sampleService.getDao().update(updateSampleBeans, ArrayUtils.asList("boardId", "boardHoleId", "locations"));
        }
        holeService.getDao().update(updateLocationHoleBeans, "sampleId", "stockType");
        locationChangeService.getDao().insert(locationChangeBeans);
        LimsLocationBoardBean boardBean = boardBeans.get(0);
        String storageType = boardBean.getExt$Item("storagetype");
        if (!StringUtils.isEmpty(storageType)){
            updateColor(storageType, boardBean);
        }
        return "success";
    }

    @Transactional
    @Override
    public void updateSampleBoardHole(RestJsonWrapperBean wrapper) {
        LimsLocationBoardHoleBean holeBean = wrapper.parseUnique(LimsLocationBoardHoleBean.class);
        // 判断更新的是板位置还是冰箱位置
        String positionType = holeBean.getExt$Item("positionType");
        if (StringUtils.isEmpty(positionType)) return;
        // 正常情况不会同时有板位置和冰箱位置，以防万一，都先清空板孔位置

        Long sampleId = holeBean.getSampleId();
        if(sampleId == null){
            return;
        }
        LimsSampleBean sampleBean = sampleService.selectByIdIfPresent(sampleId);

        LimsLocationChangeBean locationChangeBean = new LimsLocationChangeBean();
        locationChangeBean.setId(ApplicationContextHelper.getNextIdentity());
        locationChangeBean.setBusinessCode("T_LIMS_SAMPLE");
        locationChangeBean.setBusinessId(sampleId.toString());
        List<LimsLocationChangeBean> oldLocationChangeBeans = locationChangeService.selectListByFilter(SearchFilter.instance()
                .match("businessId", sampleId).filter(MatchPattern.EQ))
                .stream().sorted(Comparator.comparing(LimsLocationChangeBean::getCreatedTime).reversed()).collect(Collectors.toList());
        if (oldLocationChangeBeans != null && oldLocationChangeBeans.size() > 0){
            String oldValue = oldLocationChangeBeans.get(0).getNewValue();
            locationChangeBean.setOldValue(oldValue);
        }

        if(sampleBean != null){
            List<LimsLocationBoardHoleBean> updates = new ArrayList<>();
            Long boardId = sampleBean.getBoardId();
            if(boardId != null && sampleBean.getBoardHoleId() != null){ // 原板
                LimsLocationBoardHoleBean boardHoleBean = holeService.selectById(sampleBean.getBoardHoleId());
                boardHoleBean.setSampleId(null);
                boardHoleBean.setStockType(LimsConstant.HOLE_STOCKTYPE_FREE);
                updates.add(boardHoleBean);
            }
            LimsLocationBoardBean boardBean = selectFirstByFilter(SearchFilter.instance().match("id",holeBean.getBoardId()).filter(MatchPattern.EQ));
            String storageType = boardBean.getExt$Item("storagetype");
            if (positionType.equals("board")) {
                sampleBean.setBoardHoleId(holeBean.getId());
                sampleBean.setBoardId(holeBean.getBoardId());
                sampleBean.setLocations(holeBean.getLocations());
                // 是否修改样本实验编码，参数change=0，不修改
                String change = wrapper.getParamValue("change");
                // 不为0则修改
                if (!"0".equals(change)) {
                    // 生成实验编号：T-板编号-位置号（先横后竖，例如：T-01-01B），根据locations：板编号(竖横)，例如板01(A01)
                    if (!StringUtils.isEmpty(holeBean.getLocations())) {
                        String locations = holeBean.getLocations();
                        String board = locations.split("\\(")[0];
                        // 位置号
                        String hole = locations.split("\\(")[1];
                        sampleBean.setExpNumber("T-" + board + "-" + hole.substring(1,3) + hole.substring(0,1));
                    }
                }
                if (!"0".equals(change)) {
                    sampleService.getDao().update(sampleBean, ArrayUtils.asList("boardId", "boardHoleId", "locations", "expNumber"));
                } else {
                    sampleService.getDao().update(sampleBean, ArrayUtils.asList("boardId", "boardHoleId", "locations"));
                }
                holeBean.setStockType(LimsConstant.HOLE_STOCKTYPE_IN);
                updates.add(holeBean);
                locationChangeBean.setNewValue(boardBean.getExt$Item("locationdesc") + "-" + sampleBean.getLocations());
            }
            locationChangeService.getDao().insert(locationChangeBean);
            holeService.getDao().update(updates, "sampleId", "stockType");
            if (!StringUtils.isEmpty(storageType)){
                updateColor(storageType, boardBean);
            }
        }
    }

    @Transactional
    @Override
    public void clearSampleHole(RestJsonWrapperBean wrapper) {
        List<LimsSampleBean> sampleBeans = wrapper.parse(LimsSampleBean.class);
        // 正常情况不会同时有板位置和冰箱位置，以防万一，都先清空板孔位置
        List<Long> ids = sampleBeans.stream().map(LimsSampleBean::getId).collect(Collectors.toList());
        List<LimsLocationBoardHoleBean> boardHoleBeans = holeService.selectListBySampleIds(ids);
        sampleBeans.forEach(s -> {
            s.setBoardHoleId(null);
            s.setBoardId(null);
            s.setLocations(null);
            s.setExpNumber(null);
        });
        sampleService.getDao().update(sampleBeans, ArrayUtils.asList("boardId", "boardHoleId", "locations", "expNumber"));
        boardHoleBeans.forEach(holeBean -> {
            holeBean.setSampleId(null);
            holeBean.setStockType(LimsConstant.HOLE_STOCKTYPE_FREE);
        });
        holeService.getDao().update(boardHoleBeans,"sampleId", "stockType");

        List<Long> boardIds = boardHoleBeans.stream().map(LimsLocationBoardHoleBean::getBoardId).distinct().filter(Objects::nonNull).collect(Collectors.toList());
        List<LimsLocationBoardBean> boardBeans = selectListByFilter(SearchFilter.instance().match("id", boardIds).filter(MatchPattern.OR));
        for (LimsLocationBoardBean boardBean : boardBeans) {
            updateColor(boardBean.getExt$Item("storagetype"), boardBean);
        }
    }

    @Transactional
    @Override
    public void updateColor(String storageType, LimsLocationBoardBean locationBoardBean) {
        if (StringUtils.isEmpty(locationBoardBean.getLocationNo())) return;
        String[] locationNo = locationBoardBean.getLocationNo().split("-");
        if ("液氮罐".equals(storageType) || "冰箱".equals(storageType)) {
            // 校验盒子下板子是否全部占满
            List<LimsLocationBoardBean> locationBoardBeans = getDao().selectListByOneColumnValue(locationBoardBean.getLocationId(), "locationId");
            List<Long> boardIds = locationBoardBeans.stream().map(LimsLocationBoardBean::getId).collect(Collectors.toList());
            List<LimsLocationBoardHoleBean> boardHoleBeans = holeService.getDao().selectListByOneColumnValues(boardIds, "boardId");
            String layLocationNo = locationNo[0].concat("-").concat(locationNo[1]).concat("-").concat(locationNo[2]);

            if (boardHoleBeans.stream().allMatch(all -> !ObjectUtils.isEmpty(all.getSampleId()))) {
                limsLocationService.updateColorFlag(locationBoardBean.getLocationNo(), "2");
                // 层下板子是否全部占满
                List<LimsLocationBoardBean> boardBeans = selectListByFilter(SearchFilter.instance().match("locationNo", layLocationNo).filter(MatchPattern.SB));
                List<Long> ids = boardBeans.stream().map(LimsLocationBoardBean::getId).collect(Collectors.toList());
                List<LimsLocationBoardHoleBean> holeBeans = holeService.getDao().selectListByOneColumnValues(ids, "boardId");
                if (holeBeans.stream().allMatch(all -> !ObjectUtils.isEmpty(all.getSampleId()))) {
                    limsLocationService.updateColorFlag(layLocationNo, "2");
                } else {
                    limsLocationService.updateColorFlag(layLocationNo, "1");
                }
            } else {
                limsLocationService.updateColorFlag(layLocationNo, "1");
                limsLocationService.updateColorFlag(locationBoardBean.getLocationNo(), "1");
            }
        }
    }

    @Override
    public List<LimsLocationBoardBean> selectBoardList(LimsLocationBoardBean locationBoardBean) {
        MapperParameter temp = new MapperParameter();
        temp.setFilter(SearchFilter.instance().match("locationId",locationBoardBean.getLocationId()).filter(MatchPattern.EQ)
                .match("locationNo",locationBoardBean.getLocationNo()).filter(MatchPattern.EQ));
        List<Map<String,Object>> mapList = getDao().selectBoardList(temp);
        if (mapList.isEmpty()) return CollectionUtils.emptyList();
        List<LimsLocationBoardBean> locationBoardBeans = mapList.stream().map(m -> PersistableHelper.mapToPersistable(m, LimsLocationBoardBean.class)).collect(Collectors.toList());
        return locationBoardBeans;
    }

    @Override
    @Transactional
    public void kshAssignLocation(RestJsonWrapperBean wrapper) {
        LimsLocationBoardHoleBean holeBean = wrapper.parseUnique(LimsLocationBoardHoleBean.class);
        Long sampleId = NumberUtils.parseLong(wrapper.getParamValue("sampleId"));
        holeBean.setSampleId(sampleId);
        LimsSampleBean sampleBean = sampleService.selectByIdIfPresent(sampleId);
        LimsLocationChangeBean locationChangeBean = new LimsLocationChangeBean();
        locationChangeBean.setId(ApplicationContextHelper.getNextIdentity());
        locationChangeBean.setBusinessCode("T_LIMS_SAMPLE");
        locationChangeBean.setBusinessId(sampleId.toString());
        List<LimsLocationChangeBean> oldLocationChangeBeans = locationChangeService.selectListByFilter(SearchFilter.instance()
                .match("businessId", sampleId).filter(MatchPattern.EQ))
                .stream().sorted(Comparator.comparing(LimsLocationChangeBean::getCreatedTime).reversed()).collect(Collectors.toList());
        if (oldLocationChangeBeans != null && oldLocationChangeBeans.size() > 0) {
            String oldValue = oldLocationChangeBeans.get(0).getNewValue();
            locationChangeBean.setOldValue(oldValue);
        }
        if(sampleBean != null){
            List<LimsLocationBoardHoleBean> updates = new ArrayList<>();
            Long boardId = sampleBean.getBoardId();
            if(boardId != null && sampleBean.getBoardHoleId() != null){ // 原板
                LimsLocationBoardHoleBean boardHoleBean = holeService.selectById(sampleBean.getBoardHoleId());
                boardHoleBean.setSampleId(null);
                boardHoleBean.setStockType(LimsConstant.HOLE_STOCKTYPE_FREE);
                updates.add(boardHoleBean);
            }
            LimsLocationBoardBean boardBean = selectFirstByFilter(SearchFilter.instance().match("id",holeBean.getBoardId()).filter(MatchPattern.EQ));
            String storageType = boardBean.getExt$Item("storagetype");
            sampleBean.setBoardHoleId(holeBean.getId());
            sampleBean.setBoardId(holeBean.getBoardId());
            sampleBean.setLocations(holeBean.getLocations());
            sampleService.getDao().update(sampleBean, ArrayUtils.asList("boardId", "boardHoleId", "locations"));
            holeBean.setStockType(LimsConstant.HOLE_STOCKTYPE_IN);
            updates.add(holeBean);
            locationChangeBean.setNewValue(boardBean.getExt$Item("locationdesc") + "-" + sampleBean.getLocations());

            locationChangeService.getDao().insert(locationChangeBean);
            holeService.getDao().update(updates, "sampleId", "stockType");
            updateColor(storageType, boardBean);
        }
    }

    @Override
    @Transactional
    public void rightClickEvent(RestJsonWrapperBean wrapper) {
        List<LimsLocationBoardHoleBean> boardHoleBeans = wrapper.parse(LimsLocationBoardHoleBean.class);
    }
}
